/*
 * Copyright 2010 Christian Matzat and others
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package net.matzat.android.hiberdroid.criteria;

import net.matzat.android.hiberdroid.exception.MappingNotFoundException;
import net.matzat.android.hiberdroid.mapping.ColumnMapping;
import net.matzat.android.hiberdroid.mapping.EntityMapping;

public class Restriction {
    private enum Operator {
        EQ("=?"),
        NE("!=?"),
        GT(">?"),
        LT("<?"),
        GE(">=?"),
        LE("<=?"),
        LIKE("LIKE ?"),
        IN("IN (?)"),
        AND(" AND "),
        OR(" OR ");

        private String expression;

        private Operator(String expression) {
            this.expression = expression;
        }

        public String getExpression() {
            return expression;
        }
    }

    private Operator operator;

    // for property restrictions
    private String property;
    private Object value;

    // for and/or restrictions
    private Restriction restrictionLeft, restrictionRight;

    private Restriction(Operator operator, String property, Object value) {
        this.operator = operator;
        this.property = property;
        this.value = value;
    }

    private Restriction(Operator operator, Restriction restrictionLeft, Restriction restrictionRight) {
        this.operator = operator;
        this.restrictionLeft = restrictionLeft;
        this.restrictionRight = restrictionRight;
    }

    public static Restriction idEq(Long value) {
        return new Restriction(Operator.EQ, EntityMapping.ID_PROPERTY, value);
    }

    public static Restriction eq(String property, Object value) {
        return new Restriction(Operator.EQ, property, value);
    }

    public static Restriction ne(String property, Object value) {
        return new Restriction(Operator.NE, property, value);
    }

    public static Restriction gt(String property, Object value) {
        return new Restriction(Operator.GT, property, value);
    }

    public static Restriction lt(String property, Object value) {
        return new Restriction(Operator.LT, property, value);
    }

    public static Restriction ge(String property, Object value) {
        return new Restriction(Operator.GE, property, value);
    }

    public static Restriction le(String property, Object value) {
        return new Restriction(Operator.LE, property, value);
    }

    public static Restriction like(String property, Object value) {
        return new Restriction(Operator.LIKE, property, value);
    }

    public static Restriction in(String property, String... values) {
        StringBuffer valuesInLine = new StringBuffer();
        boolean isFirst = true;
        for (String value : values) {
            if (!isFirst) {
                valuesInLine.append(", ");
            }
            valuesInLine.append(value);
            isFirst = false;
        }
        return new Restriction(Operator.IN, property, valuesInLine.toString());
    }

    public static Restriction or(Restriction restrictionLeft, Restriction restrictionRight) {
        return new Restriction(Operator.OR, restrictionLeft, restrictionRight);
    }

    public static Restriction and(Restriction restrictionLeft, Restriction restrictionRight) {
        return new Restriction(Operator.AND, restrictionLeft, restrictionRight);
    }

    public String getExpression(EntityMapping entityMapping) {
        if (operator == Operator.OR || operator == Operator.AND) {
            return restrictionLeft.getExpression(entityMapping) + operator.getExpression() + restrictionRight.getExpression(entityMapping);
        }
        if (property.equals(EntityMapping.ID_PROPERTY)) {
            property = entityMapping.getIdProperty();
        }
        ColumnMapping columnMapping = entityMapping.getColumns().get(property);
        if (columnMapping == null) {
            throw new MappingNotFoundException(String.format("Mapping for property %s not found.", property));
        }
        return columnMapping.getColumn() + operator.getExpression();
    }

    public String getValue() {
        return value.toString();
    }
}
